package cn.yikaojiuguo.student.ui.ask;

import android.app.Activity;
import android.app.Service;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.AmrInputStream;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;

import cn.yikaojiuguo.student.R;
import cn.yikaojiuguo.student.app.YiKaoApplication;
import cn.yikaojiuguo.student.ui.view.TimerView;

public class RecordAndDraw {

	// 录音采样频率
	private int frequency;
	// 声道
	private int channelConfiguration;
	// 音频数据格式
	private int audioEncoding;
	private Activity mContext;
	private AudioRecord audioRecord;
	private int recBufSize;
	private SurfaceView sfv;
	private Paint mPaint;
	// 录音线程控制标记
	public boolean isRecording = false;
	// 录音线程控制标记
	private boolean isWriting = false;
	private float strokeWidth;
	public ArrayList<Short> inBuf = new ArrayList<Short>();
	private ArrayList<Short> allInBuf = new ArrayList<Short>();

	public ArrayList<Short> redrawBuf = new ArrayList<Short>();
	private ArrayList<Short> firstScreenBuf = new ArrayList<Short>();
	private LinkedList<byte[]> write_data = new LinkedList<byte[]>();
	private ArrayList<Short> cloneBuf;
	private ArrayList<Short> backUpList;
	private boolean isBackUp = false;
	private boolean isStop = false;
	private boolean isPlay = false;
	private boolean isOutOfFirstScreen = false;
	// x轴缩小比例
	public int rateX = 400;
	// y轴缩小比例
	public int rateY = 0;
	// 两次绘制的间隔距离
	private int divider = 2;
	public int baseLine = 0;
	private String tempName = YiKaoApplication.getCacheVoicePath() + "/ask.pcm";
	private int sampleRateInHz = 8000;
	private OnFinishRecordListener listener = null;
	public File amrFile;
	private int width;
	public Timer redrawTimer;
	public int endIndex = 0;
	public MediaPlayer player = new MediaPlayer();
	private TimerTask timerTask;
	private TimerView timerView;
	private boolean isBegain = true;
	private int height;
	private AudioManager mAudioManager;
	private int testTime = 3;

	public RecordAndDraw(Activity context, int frequency, int channelConfiguration, int
			audioEncoding, SurfaceView sfv,
						 TimerView timerView) {
		this.mContext = context;
		this.frequency = frequency;
		this.channelConfiguration = channelConfiguration;
		this.audioEncoding = audioEncoding;
		this.sfv = sfv;
		this.timerView = timerView;
		sfv.getHolder().addCallback(new Callback() {

			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
				if (isPlay) {
					backUpList = redrawBuf;
				} else {
					backUpList = cloneBuf;
				}
				isBackUp = true;
			}

			@Override
			public void surfaceCreated(SurfaceHolder holder) {
				height = holder.getSurfaceFrame().height();
				rateY = 10000 / height;
				width = holder.getSurfaceFrame().width();
				baseLine = holder.getSurfaceFrame().top + height / 2;
				if (isBackUp && isStop) {
					isBackUp = false;
					isStop = false;
					if (isOutOfFirstScreen || !isPlay) {
						reDraw(backUpList, new ArrayList<Short>());
					} else {
						reDraw(backUpList, firstScreenBuf);
					}
					drawDeleteButton();
				}
			}

			@Override
			public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
			}
		});
		recBufSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
		audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding,
				recBufSize);
		mPaint = new Paint();
		mPaint.setColor(0xFF4A90E2);// 设置画笔颜色
		mPaint.setStrokeWidth(4f);// 设置画笔粗细
		mPaint.setAntiAlias(true);
		strokeWidth = mPaint.getStrokeWidth();
		player = new MediaPlayer();
		audioRecord.startRecording();
		mAudioManager = (AudioManager) mContext.getSystemService(Service.AUDIO_SERVICE);
	}

	public boolean checkPermission() {
		byte[] buffer = new byte[recBufSize];
		short sum = 0;
		for (int i = 0; i < testTime; i++) {
			audioRecord.read(buffer, 0, recBufSize);
			sum += calculateAverageAmp(buffer);
		}
		if (sum == 0) {
			return false;
		} else {
			return true;
		}
	}

	public void start() {
		mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
		mAudioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, true);
		isRecording = true;
		isWriting = true;
		new Thread(new WriteRunnable()).start();
		RecordTask recordTask = new RecordTask();
		recordTask.execute();

	}

	public void clear() {
		inBuf.clear();
		allInBuf.clear();
		firstScreenBuf.clear();
		redrawBuf.clear();
		if (redrawTimer != null) {
			redrawTimer.cancel();
		}
		endIndex = 0;
		Canvas canvas = sfv.getHolder().lockCanvas(new Rect(0, 0, sfv.getWidth(), sfv.getHeight()));// 关键:获取画布
		canvas.drawColor(0xff464646);// 清除背景
		sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布，提交画好的图像
	}

	public void stop() {
		isRecording = false;
		mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
		mAudioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, false);
		drawDeleteButton();
	}

	public void release() {
		try {
			isRecording = false;
			player.stop();
			player.release();
			player = null;
			audioRecord.stop();
			audioRecord.release();
			audioRecord = null;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void playAndDraw() {
		isPlay = true;
		isOutOfFirstScreen = false;
		player.reset();
		try {
			player.setDataSource(YiKaoApplication.getCacheVoicePath() + "/ask.amr");
			redrawTimer = new Timer();
			timerTask = new TimerTask() {
				@Override
				public void run() {
					mContext.runOnUiThread(new Runnable() {

						@Override
						public void run() {
							if (player.isPlaying()) {
								if (endIndex < allInBuf.size()) {
									redrawBuf.add(allInBuf.get(endIndex));
									reDraw(redrawBuf, firstScreenBuf);
									endIndex++;
								} else {
									redrawTimer.cancel();
									isPlay = false;
									drawDeleteButton();
								}
							}
						}
					});
				}
			};

			player.prepare();
			// (5) 设置准备监听
			player.setOnPreparedListener(new OnPreparedListener() {
				@Override
				public void onPrepared(MediaPlayer mp) {
					// (6)开始播放
					player.start();
					redrawTimer.scheduleAtFixedRate(timerTask, 0, player.getDuration() / allInBuf.size());
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void drawDeleteButton() {
		Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.record_delete);
		Matrix matrix = new Matrix();
		matrix.postScale(0.4f, 0.4f);
		Bitmap copy = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
				matrix, true);
		int paddingTop = (height - copy.getHeight()) / 2;
		int paddingLeft = (width - copy.getWidth()) / 2;
		Canvas canvas = sfv.getHolder().lockCanvas(
				new Rect(paddingLeft, paddingTop, paddingLeft + copy.getWidth(), paddingTop + copy.getHeight()));
		canvas.drawBitmap(copy, paddingLeft, paddingTop, null);
		sfv.getHolder().unlockCanvasAndPost(canvas);
	}

	private short calculateAverageAmp(byte[] buffer) {
		int returnval = 0;
		for (int i = 0; i < buffer.length - 1; i += 2) {
			returnval += Math.abs((short) ((0x0000 | buffer[i + 1]) << 8 | buffer[i]));
		}
		return (short) (returnval / (buffer.length / 2));
	}

	/**
	 * 异步录音程序
	 */
	private class RecordTask extends AsyncTask<Object, Object, Object> {

		@Override
		protected Object doInBackground(Object... params) {
			try {
				byte[] buffer = new byte[recBufSize];
				// audioRecord.startRecording(); // 开始录制
				while (isRecording) {
					// 从MIC保存数据到缓冲区
					// synchronized (inBuf) {
					// 添加数据
					int readsize = audioRecord.read(buffer, 0, recBufSize);
					if (AudioRecord.ERROR_INVALID_OPERATION != readsize) {
						synchronized (write_data) {
							write_data.add(buffer);
						}
					}
					Short averageShort = calculateAverageAmp(buffer);
					allInBuf.add(averageShort);
					inBuf.add(averageShort);
					if (firstScreenBuf.size() - divider <= width / (divider + strokeWidth)) {
						firstScreenBuf.add(averageShort);
					}
					// }
					cloneBuf = new ArrayList<Short>();
					// synchronized (inBuf) {
					if (inBuf.size() == 0)
						return null;
					while (inBuf.size() - divider > sfv.getWidth() / (divider + strokeWidth)) {
						inBuf.remove(0);
					}
					cloneBuf = (ArrayList<Short>) inBuf.clone();// 保存
					// }
					if (isRecording) {
						if (isBegain) {
							isBegain = false;
							timerView.start();
						}
						simpleDraw(cloneBuf);// 把缓冲区数据画出来
					}
				}
				isBegain = true;
				isWriting = false;
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(Object... values) {
			super.onProgressUpdate(values);
		}
	}

	public void drawBackUp() {
		isStop = true;
	}

	/**
	 * 绘制指定区域
	 * 
	 * @param buf
	 *            缓冲区
	 */
	public void simpleDraw(ArrayList<Short> buf) {
		Canvas canvas = sfv.getHolder().lockCanvas();
		if (canvas == null) {
			return;
		}
		canvas.drawColor(0xff464646);// 清除背景
		int start = 0;
		int y;
		for (int i = 0; i < buf.size(); i++) {
			y = Math.abs(buf.get(i) / rateY);
			if (y > sfv.getHeight() / 2 * 4 / 5) {
				y = sfv.getHeight() / 2 * 4 / 5;
			}
			mPaint.setStrokeWidth(1);
			canvas.drawLine(0, baseLine, start + (i - 1) * (divider + strokeWidth), baseLine, mPaint);
			mPaint.setStrokeWidth(4);
			canvas.drawLine(start + (i - 1) * (divider + strokeWidth), baseLine - y, start + (i - 1)
					* (divider + strokeWidth), baseLine + y, mPaint);
		}
		sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布，提交画好的图像
	}

	/**
	 * 异步写文件
	 */
	class WriteRunnable implements Runnable {

		@Override
		public void run() {
			File file = null;
			try {
				FileOutputStream fos = null;
				try {
					file = new File(tempName);
					if (file.exists()) {
						file.delete();
					}
					fos = new FileOutputStream(file);// 建立一个可存取字节的文件
				} catch (Exception e) {
					e.printStackTrace();
				}
				while (isWriting || write_data.size() > 0) {
					byte[] buffer = null;
					if (write_data.size() > 0) {
						synchronized (write_data) {
							// if (write_data.size() > 0) {
							buffer = write_data.remove(0);
							// }
						}
					}
					try {
						if (buffer != null) {
							fos.write(buffer);
							fos.flush();
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				fos.close();// 关闭写入流
				copyWaveFile(tempName, YiKaoApplication.getCacheVoicePath() + "/ask.wav");
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (file.exists()) {
				file.delete();
			}
		}
	}

	// 这里得到可播放的音频文件
	private void copyWaveFile(String inFilename, String outFilename) {
		FileInputStream in = null;
		FileOutputStream out = null;
		long totalAudioLen = 0;
		long totalDataLen = totalAudioLen + 36;
		long longSampleRate = sampleRateInHz;
		int channels = 2;
		long byteRate = 16 * sampleRateInHz * channels / 8;
		byte[] data = new byte[recBufSize];
		try {
			in = new FileInputStream(inFilename);
			out = new FileOutputStream(outFilename);
			totalAudioLen = in.getChannel().size();
			totalDataLen = totalAudioLen + 36;
			WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
			while (in.read(data) != -1) {
				out.write(data);
			}
			in.close();
			out.close();
			convertWavToAmr(outFilename, YiKaoApplication.getCacheVoicePath() + "/ask.amr");
			File file = new File(outFilename);
			if (file.exists()) {
				file.delete();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void reDraw(ArrayList<Short> redrawBuf, ArrayList<Short> firstScreenBuf) {
		Canvas canvas = sfv.getHolder().lockCanvas();
		if (canvas == null) {
			return;
		}
		canvas.drawColor(0xff464646);// 清除背景
		int start = 0;

		int y2;
		mPaint.setColor(0xFF7B7B7B);
		if (endIndex < firstScreenBuf.size()) {
			for (int i = 0; i < firstScreenBuf.size(); i++) {
				y2 = Math.abs(firstScreenBuf.get(i) / rateY);
				if (y2 > sfv.getHeight() / 2 * 4 / 5) {
					y2 = sfv.getHeight() / 2 * 4 / 5;
				}
				mPaint.setStrokeWidth(1);
				canvas.drawLine(0, baseLine, start + (i - 1) * (divider + strokeWidth), baseLine, mPaint);
				mPaint.setStrokeWidth(4);
				canvas.drawLine(start + (i - 1) * (divider + strokeWidth), baseLine - y2, start + (i - 1)
						* (divider + strokeWidth), baseLine + y2, mPaint);
			}
		} else {
			isOutOfFirstScreen = true;
		}
		mPaint.setColor(0xFF4A90E2);
		if (redrawBuf.size() - divider > sfv.getWidth() / (divider + strokeWidth)) {
			redrawBuf.remove(0);
		}
		int y;
		for (int i = 0; i < redrawBuf.size(); i++) {
			y = Math.abs(redrawBuf.get(i) / rateY);
			if (y > sfv.getHeight() / 2 * 4 / 5) {
				y = sfv.getHeight() / 2 * 4 / 5;
			}
			mPaint.setStrokeWidth(1);
			canvas.drawLine(0, baseLine, start + (i - 1) * (divider + strokeWidth), baseLine, mPaint);
			mPaint.setStrokeWidth(4);
			canvas.drawLine(start + (i - 1) * (divider + strokeWidth), baseLine - y, start + (i - 1)
					* (divider + strokeWidth), baseLine + y, mPaint);
		}
		sfv.getHolder().unlockCanvasAndPost(canvas);// 解锁画布，提交画好的图像
	}

	public void playPause() {
		player.pause();
		drawDeleteButton();
	}

	public void playStart() {
		player.start();
	}

	public void convertWavToAmr(String wavPath, String amrPath) {
		InputStream inStream = null;
		AmrInputStream aStream = null;
		OutputStream out = null;
		amrFile = null;
		try {
			inStream = new FileInputStream(wavPath);
			aStream = new AmrInputStream(inStream);

			amrFile = new File(amrPath);
			if(amrFile.exists()) {
				amrFile.delete();
			}
			amrFile.createNewFile();
			out = new FileOutputStream(amrFile);

			out.write(0x23);
			out.write(0x21);
			out.write(0x41);
			out.write(0x4D);
			out.write(0x52);
			out.write(0x0A);

			byte[] x = new byte[1024];
			int len;
			while ((len = aStream.read(x)) > 0) {
				out.write(x, 0, len);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				inStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				aStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		if (amrFile != null && amrFile.exists() && listener != null) {
			listener.onFinishRecord(amrFile);
			listener = null;
		}

	}

	public void setOnRecordFinishListener(OnFinishRecordListener listener) {
		this.listener = listener;
	}

	/**
	 * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。 为我为啥插入这44个字节，这个还真没深入研究，不过你随便打开一个wav
	 * 音频的文件，可以发现前面的头文件可以说基本一样哦。每种格式的文件都有 自己特有的头文件。
	 */
	private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate,
			int channels, long byteRate) throws IOException {
		byte[] header = new byte[44];
		header[0] = 'R'; // RIFF/WAVE header
		header[1] = 'I';
		header[2] = 'F';
		header[3] = 'F';
		header[4] = (byte) (totalDataLen & 0xff);
		header[5] = (byte) ((totalDataLen >> 8) & 0xff);
		header[6] = (byte) ((totalDataLen >> 16) & 0xff);
		header[7] = (byte) ((totalDataLen >> 24) & 0xff);
		header[8] = 'W';
		header[9] = 'A';
		header[10] = 'V';
		header[11] = 'E';
		header[12] = 'f'; // 'fmt ' chunk
		header[13] = 'm';
		header[14] = 't';
		header[15] = ' ';
		header[16] = 16; // 4 bytes: size of 'fmt ' chunk
		header[17] = 0;
		header[18] = 0;
		header[19] = 0;
		header[20] = 1; // format = 1
		header[21] = 0;
		header[22] = (byte) channels;
		header[23] = 0;
		header[24] = (byte) (longSampleRate & 0xff);
		header[25] = (byte) ((longSampleRate >> 8) & 0xff);
		header[26] = (byte) ((longSampleRate >> 16) & 0xff);
		header[27] = (byte) ((longSampleRate >> 24) & 0xff);
		header[28] = (byte) (byteRate & 0xff);
		header[29] = (byte) ((byteRate >> 8) & 0xff);
		header[30] = (byte) ((byteRate >> 16) & 0xff);
		header[31] = (byte) ((byteRate >> 24) & 0xff);
		header[32] = (byte) (2 * 16 / 8); // block align
		header[33] = 0;
		header[34] = 16; // bits per sample
		header[35] = 0;
		header[36] = 'd';
		header[37] = 'a';
		header[38] = 't';
		header[39] = 'a';
		header[40] = (byte) (totalAudioLen & 0xff);
		header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
		header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
		header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
		out.write(header, 0, 44);
	}

	public interface OnFinishRecordListener {
		void onFinishRecord(File file);
	}
}
